%%%-------------------------------------------------------------------
%%% @author liuwentao
%%% @doc
%%%     角色增损益
%%% @end
%%% Created : 19. 7月 2021 21:22
%%%-------------------------------------------------------------------
-module(role_gain).
-author("liuwentao").

-export([
    do/2
    , do/3
    , do_opt/3
    , do_notice/2
    , do_notice/3
    , do_notice/4
    , do_notice/5
]).

-include("common.hrl").
-include("role.hrl").
-include("gain.hrl").
-include("trigger.hrl").
-include("conn.hrl").
-include("item.hrl").
-include("notice.hrl").
-include("package.hrl").


-define(evts, role_gain_evts).
-define(add_evts(GainOrLoss),
    %% 如果是在role_gain:do里面再调用了role_gain:do就可能出现问题,所以这里加了兼容处理
    OldEvts =
        case get(?evts) of
            E when is_list(E) -> E;
            _ -> []
        end,
    Evts = merge_evts(GainOrLoss, OldEvts),
    put(?evts, Evts)
).


%% ----------------------------------------------------
%% 外部接口
%% ----------------------------------------------------
%% @doc 执行损益并发送相关信息
-spec do_notice(list(), #role{}) -> {ok, #role{}} | {false, #loss{} | #gain{}}.
do_notice(List, Role) ->
    do_notice(List, Role, "").
do_notice(List, Role, Title) ->
    do_notice(List, Role, Title, "").
do_notice(List, Role, Title, Tail) ->
    do_notice(List, Role, Title, Tail, []).
do_notice(List, Role, Title, Tail, Ext) ->
    case to_do(List, Role) of
        {ok, NRole, NewList, #role_gain_gl_args{ext_args = _ExtArgs}} ->
            {ok, NRole};
        Other ->
            Other
    end.

%% @doc 执行损益列表处理
%% 整个列表构成一个事务，如果其中某一个处理失败，则放弃前面所有操作成功的项
do(List, Role) ->
    do(List, Role, []).
do([], Role, _Ext) ->
    {ok, Role};
do(List, Role, _Ext) ->
    case to_do(List, Role) of
        {ok, NewRole, _NewL, _} ->
            {ok, NewRole};
        {false, L} ->
            {false, L}
    end.

%%do(List, Role) ->
%%    do(List, Role, #{}).
%% @doc 带参数的执行损益列表处理
%% 整个列表构成一个事务，如果其中某一个处理失败，则放弃前面所有操作成功的项
-spec do_opt(List :: list(), Role :: #role{}, Opts :: map()) -> {ok, #role{}, map()} | {false, #loss{} | #gain{}} | {false, string()}.
do_opt([], Role, Opts) ->
    {ok, Role, Opts};
do_opt(List, Role, Opts) when is_map(Opts) ->
    case to_do(List, Role) of
        {ok, NewRole, NewL, _} ->
            {ok, NewRole, Opts#{new_gl => NewL}};
        {false, L} ->
            {false, L}
    end;
do_opt(_List, #role{name = Name}, Opts) ->
    ?ERR("[name:~ts]错误损益操作参数:~w", [Name, Opts]),
    {false, ?T("错误的参数")}.

%% 损益操作处理
to_do(List, Role = #role{}) ->
    fun_call:trans_begin(),
    put(?evts, []),
    %% 成功的GL, 更新参数(更新列表, 更新参数, 额外参数)
    case gl_all(List, Role, [], #role_gain_gl_args{}) of
        {ok, GlRole, NewGL, Args} ->
            refresh(Args, GlRole),
            GainRole = GlRole#role{is_gain = true},
            NRole =
                case erase(?evts) of
                    Evts when is_list(Evts) ->
                        %% 如果是在role_gain:do里面再调用了role_gain:do就可能出现问题
                        role_trigger:fire(Evts, GainRole);
                    _ -> GainRole
                end,
            fun_call:trans_flush(),
            {ok, NRole, NewGL, Args};
        {false, L} ->
            erase(?evts),
            fun_call:trans_clean(),
            case L of
                #loss{label = _Label} ->
                    ok;
                _ -> ok
            end,
            {false, L}
    end.

%% 损益操作刷新
refresh(
    #role_gain_gl_args{
        refresh_items = RefreshList, push_assets = PushArgs,
        ext_args = ExtArgs
    },
    Role
) ->
    package:push_gain(RefreshList, Role).

%% 合并损益事件
merge_evts([], Acc) ->
    Acc;
merge_evts([I | T], Acc) ->
    merge_evts(T, [I | Acc]);
merge_evts(Single, Acc) ->
    [Single | Acc].

%% 转换为添加事件
to_gain_evt([], Acc) ->
    Acc;
to_gain_evt([I | T], Acc) ->
    case to_gain_evt(I) of
        false ->
            to_gain_evt(T, Acc);
        Evt ->
            to_gain_evt(T, [Evt | Acc])
    end.
to_gain_evt(#item{base_id = BaseId, quantity = Num}) ->
    #evt_get_item{base_id = BaseId, num = Num};
to_gain_evt({BaseId, _Bind, Num}) ->
    #evt_get_item{base_id = BaseId, num = Num};
to_gain_evt(_Unkown) -> false.


%% @doc 损益操作的刷新，列表全部成功之后才推送，避免回滚带来的问题
-spec gl_all(GL, Role, AccGL, Args) -> {ok, NewRole, NewGL, NewArgs} | {false, False} when
    GL :: [#gain{} | #loss{}],
    AccGL :: [#gain{} | #loss{}],
    NewGL :: [#gain{} | #loss{}],
    Role :: #role{},
    NewRole :: #role{},
    False :: #gain{} | #loss{},
    Args :: #role_gain_gl_args{},
    NewArgs :: #role_gain_gl_args{}.

gl_all(
    [], Role, GL,
    Args = #role_gain_gl_args{
        push_assets = PushAssets, refresh_items = RefreshItems
    }
) ->
    NewGL = case GL of [_ | _] -> lists:reverse(GL); _ -> [] end,
    NewArgs = Args#role_gain_gl_args{
        push_assets = PushAssets, refresh_items = RefreshItems
    },
    {ok, Role, NewGL, NewArgs};
gl_all([H | T], Role, NewGL, Args) ->
    case gl(H, Args, Role) of
        {false, L} -> {false, L};
        {ok, NewRole} ->
            gl_all(T, NewRole, [H | NewGL], Args);
        {ok, NewRole, NewArgs} ->
            gl_all(T, NewRole, [H | NewGL], NewArgs);
        {ok, NewRole, GL = [_ | _], NewArgs} ->
            gl_all(T, NewRole, GL ++ NewGL, NewArgs);
        {ok, NewRole, [], NewArgs} ->
            gl_all(T, NewRole, NewGL, NewArgs);
        _Unkown ->
            ?ERR("错误的损益操作的刷新返回值[Err:~w]", [_Unkown]),
            {false, H}
    end.



%% ----------------------------------------------------
%% 私有函数
%% ----------------------------------------------------
%% 数值异常过滤
gl(G = #gain{val = Val}, _Args, _Role) when is_integer(Val) andalso Val < 0 ->
    {false, G#gain{msg = ?T("数值异常")}};
gl(L = #loss{val = Val}, _Args, _Role) when is_integer(Val) andalso Val < 0 ->
    {false, L#loss{msg = ?T("数值异常")}};
gl(
    G = #gain{label = item_base_id, val = ItemInfo},
    Args = #role_gain_gl_args{},
    Role = #role{name = Name}
) when is_list(ItemInfo) ->
    case gain_item(Role, by_base_id, ItemInfo, Args) of
        {false} ->
            ?ERR("[~ts]获得物品时发生错误，无法生成物品列表:~w", [Name, ItemInfo]),
            {false, G#gain{reason = error_item}};
        {false, full} -> {false, G#gain{reason = bag_full, msg = ?T("背包已满，增加物品失败")}};
        {false, Msg} -> {false, G#gain{msg = Msg}};
        {ok, NRole, NewArgs, AllItems} -> {ok, NRole, [G#gain{label = items, val = AllItems}], NewArgs}
    end;

%% 获得物品
%% Items = [#item{} | ..]
gl(G = #gain{label = items, val = Items}, Args = #role_gain_gl_args{}, Role)
    when is_list(Items) ->
    case gain_item(Role, by_items, Items, Args) of
        {false} -> {false, G#gain{reason = error_item}};
        {false, full} -> {false, G#gain{reason = bag_full, msg = ?T("背包已满，增加物品失败")}};
        {false, Msg} -> {false, G#gain{msg = Msg}};
        {ok, NRole, NewArgs, AllItems} -> {ok, NRole, [G#gain{label = items, val = AllItems}], NewArgs}
    end;



%% ============================================================
%% 通用资产增加删除逻辑
%% ============================================================
%% @doc 获得资产
gl(
    G = #gain{label = Label, val = Val, source = _Source, reason = R},
    Args = #role_gain_gl_args{push_assets = PushAssets},
    Role = #role{name = Name}
) ->
    case assets_data:label_to_enum(Label) of
        0 ->
            ?ERR("角色[~ts]收到无法处理的损益数据: ~w", [Name, G]),
            {false, G};
        Id ->
            %%获取旧资产
            OldVal = assets:get_role_assets(Role, Id),
            NewRole = assets:update_role_assets(Role, Id, round(OldVal + Val)),
            %% 增加触发器
            case assets:gain_to_trigger(Id, round(Val), R) of
                false ->
                    ok;
                Evt ->
                    ?add_evts(Evt)
            end,
            add_log_item(gain, {Id, Val}),
            NewPushAssets = [rest | PushAssets],
            NewArgs = Args#role_gain_gl_args{push_assets = NewPushAssets},
            {ok, NewRole, NewArgs}
    end;


%% @doc 失去资产
gl(
    L = #loss{label = Label, val = Val, reason = R, source = _Source},
    Args = #role_gain_gl_args{push_assets = PushAssets},
    Role = #role{name = Name}
) when Val >= 0 ->
    case assets_data:label_to_enum(Label) of
        0 ->
            ?ERR("角色[~ts]收到无法处理的损益数据: ~w", [Name, L]),
            {false, L};
        Id ->
            %%获取旧资产
            OldVal = assets:get_role_assets(Role, Id),
            case OldVal < Val of
                true -> {false, L};
                false ->
                    NewRole = assets:update_role_assets(Role, Id, round(OldVal - Val)),
                    %% 增加触发器
                    case assets:loss_to_trigger(Id, Val, R) of
                        false ->
                            ok;
                        Evt ->
                            ?add_evts(Evt)
                    end,
                    add_log_item(loss, {Id, Val}),
                    NewPushAssets = [rest | PushAssets],
                    NewArgs = Args#role_gain_gl_args{push_assets = NewPushAssets},
                    {ok, NewRole, NewArgs}
            end
    end;


%% 容错处理
gl(G, _Args, #role{name = Name}) ->
    ?ERR("角色[~ts]收到无法处理的损益数据: ~w", [Name, G]).




%% 日志道具信息
add_log_item(_Label, []) -> ok;
add_log_item(Label, [I | T]) ->
    add_log_item(Label, I),
    add_log_item(Label, T);
add_log_item(Label, {BaseId, Num}) ->
    put(role_gain_log_items, [{Label, BaseId, Num} | get(role_gain_log_items)]);
add_log_item(Label, {BaseId, _, Num}) ->
    put(role_gain_log_items, [{Label, BaseId, Num} | get(role_gain_log_items)]);
add_log_item(Label, #item{base_id = BaseId, quantity = Num}) ->
    put(role_gain_log_items, [{Label, BaseId, Num} | get(role_gain_log_items)]);
add_log_item(_Label, _) -> skip.

